package com.daffodil.storage.service.impl;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import com.daffodil.storage.client.SftpStorageClient;
import com.daffodil.storage.exception.BaseException;
import com.daffodil.storage.model.MediaMimetype;
import com.daffodil.storage.model.Page;
import com.daffodil.storage.model.StorageResult;
import com.daffodil.storage.properties.SftpProperties;
import com.daffodil.storage.properties.StorageProperties;
import com.daffodil.storage.service.AStorageService;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.ChannelSftp.LsEntrySelector;
import com.jcraft.jsch.SftpATTRS;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 使用SFTP方式存储文件，本质也是磁盘存储
 * <p>SFTP: Secure File Transfer Protocol 安全文件传输协议</p>
 * @author yweijian
 * @date 2023年11月3日
 * @version 1.0
 * @description
 */
@Slf4j
@Service
@Configuration
@ConditionalOnProperty(name = StorageProperties.NAME, havingValue = StorageProperties.SFTP)
public class StorageSftpServiceImpl extends AStorageService {

    @Autowired
    private SftpStorageClient client;

    @Autowired
    private StorageProperties storageProperties;

    /** 限定列举文件的最大个数 */
    private static final Integer MAXKEYS = 1000;

    @Override
    public List<StorageResult> list(String folder, String name, Page page) {
        List<StorageResult> results = new ArrayList<StorageResult>();
        SftpProperties properties = storageProperties.getSftp();
        StringBuffer prefix = new StringBuffer();
        prefix.append(properties.getBucketName());
        prefix.append(StrUtil.SLASH);
        if(ObjectUtil.isNotEmpty(folder)) {
            //目录不合法
            if(folder.contains(StrUtil.DOT)) {
                return results;
            }
            folder = folder.startsWith(StrUtil.SLASH) ? folder.substring(1) : folder;
            prefix.append(folder);
        }
        final String keyPrefix = folder;

        //目录数据
        LsEntrySelector folderSelector = (entry) -> {
            SftpATTRS attrs = entry.getAttrs();
            if(attrs.isDir() && !StrUtil.DOT.equals(entry.getFilename()) && !StrUtil.DOUBLE_DOT.equals(entry.getFilename())) {
                String key = keyPrefix + entry.getFilename() + StrUtil.SLASH;
                StorageResult result = StorageResult.builder().name(FileNameUtil.getName(key)).origin(FileNameUtil.getName(key))
                        .prefix(FileNameUtil.getPrefix(key)).suffix(FileNameUtil.getSuffix(key)).size(0L).mime(StrUtil.EMPTY).key(key)
                        .url(properties.getDomain() + StrUtil.SLASH + key).metadata(MapUtil.empty()).build();
                results.add(result);
            }
            return LsEntrySelector.CONTINUE;
        };
        client.list(prefix.toString(), folderSelector);

        //对象数据
        List<LsEntry> entrys = new ArrayList<LsEntry>();
        LsEntrySelector fileSelector = (entry) -> {
            SftpATTRS attrs = entry.getAttrs();
            if(attrs.isReg() && entrys.size() < MAXKEYS) {
                if(ObjectUtil.isNotEmpty(name)) {
                    if(entry.getFilename().matches(".*" + name + ".*")) {
                        entrys.add(entry);
                    }
                }else {
                    entrys.add(entry);
                }
            }
            return LsEntrySelector.CONTINUE;
        };
        client.list(prefix.toString(), fileSelector);

        page.setTotalRow(entrys.size());
        int fromIndex = page.getFromIndex();
        int toIndex = Math.min(page.getToIndex(), entrys.size());
        if(fromIndex > toIndex) {
            return results;
        }

        LsEntry[] files = Arrays.copyOfRange(entrys.toArray(new LsEntry[entrys.size()]), fromIndex, toIndex);

        for(int i = 0; i < files.length; i++) {
            LsEntry entry = files[i];
            SftpATTRS attrs = entry.getAttrs();
            String key = keyPrefix + StrUtil.SLASH + entry.getFilename();
            StorageResult result = StorageResult.builder().name(FileNameUtil.getName(key)).origin(FileNameUtil.getName(key))
                    .prefix(FileNameUtil.getPrefix(key)).suffix(FileNameUtil.getSuffix(key)).size(attrs.getSize())
                    .mime(MediaMimetype.getInstance().getMimetype(FileNameUtil.getSuffix(key))).key(key)
                    .url(properties.getDomain() + StrUtil.SLASH + key).metadata(MapUtil.empty()).build();
            results.add(result);
        }

        return results;
    }

    @Override
    protected StorageResult upolad(StorageResult result, InputStream inputStream) {
        log.debug("Sftp传输对象存储 > 上传对象 > fileName={}", result.getOrigin());

        SftpProperties properties = storageProperties.getSftp();
        result.setUrl(properties.getDomain() + StrUtil.SLASH + result.getKey());

        String filePath = properties.getBucketName() + StrUtil.SLASH + result.getKey();
        client.upload(filePath, inputStream);
        log.debug("Sftp传输对象存储 > 上传对象 > file={}", filePath);

        log.debug("Sftp传输对象存储 > 上传对象 > result={}", result.toString());
        return result;
    }

    @Override
    public void download(String key, HttpServletResponse response) {
        String fileName = FileNameUtil.getName(key);
        if(ObjectUtil.isEmpty(fileName)) {
            return;
        }
        SftpProperties properties = storageProperties.getSftp();
        String filePath = properties.getBucketName() + StrUtil.SLASH + key;

        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(fileName, StandardCharsets.UTF_8).build().toString());
        try {
            InputStream inputStream = client.get(filePath);
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BaseException(e.getMessage());
        }
    }

    @Override
    public void remove(String key) {
        SftpProperties properties = storageProperties.getSftp();
        String filePath = properties.getBucketName() + StrUtil.SLASH + key;
        client.delete(filePath);
    }

}
